package com.ics.cmsadmin.modules.wechat.service.impl;

import com.google.gson.JsonSyntaxException;
import com.ics.cmsadmin.modules.wechat.config.WechatUrlConfig;
import com.ics.cmsadmin.modules.wechat.constanst.Constant;
import com.ics.cmsadmin.modules.wechat.model.auth.resp.*;
import com.ics.cmsadmin.modules.wechat.model.param.AbstractParams;
import com.ics.cmsadmin.modules.wechat.result.ResultState;
import com.ics.cmsadmin.modules.wechat.service.WechatAuthService;
import com.ics.cmsadmin.modules.wechat.utils.HttpReqUtil;
import com.ics.cmsadmin.modules.wechat.utils.JsonUtil;
import com.ics.cmsadmin.modules.wechat.utils.MD5Util;
import com.ics.cmsadmin.modules.wechat.utils.SignatureUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

/**
 * Wechat Auth Service
 *
 * @author phil
 * @date 2017年7月9日
 */
@Slf4j
@Service
public class WechatAuthServiceImpl implements WechatAuthService {

	private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private com.ics.cmsadmin.frame.property.WechatConfig wechatConfig;

    @Autowired
    private StringRedisTemplate redisTemplate;

	/**
	 * 获取授权凭证token
	 *
	 * @param key
	 *            应用appid
	 * @param secret
	 *            应用密匙
	 * @return json格式的字符串
	 */
	@Override
	public String getAccessToken() {
        String result = redisTemplate.opsForValue().get(Constant.WECHAT_ACCESS_TOKEN);
        if (StringUtils.isNotBlank(result)) {
            return result;
        }
        TreeMap<String, String> map = new TreeMap<>();
        map.put("grant_type", "client_credential");
        map.put("appid", wechatConfig.appId); // WechatUrlConfig.APP_ID
        map.put("secret", wechatConfig.appSecret); // WechatUrlConfig.APP_SECRET
        String json = HttpReqUtil.HttpDefaultExecute(Constant.GET_METHOD, WechatUrlConfig.GET_ACCESS_TOKEN_URL, map,
				"", null);
		AccessToken accessToken = JsonUtil.fromJsonString(json, AccessToken.class);
		if (accessToken != null) {
			result = accessToken.getAccess_token();
            redisTemplate.opsForValue().set(Constant.WECHAT_ACCESS_TOKEN, result, accessToken.getExpires_in() - 20, TimeUnit.SECONDS);
        }
		return result;
	}

	/**
	 * 获取授权请求url
	 *
	 * @param basic
	 * @param url
	 * @return
	 * @throws Exception
	 */
	public String getAuthPath(AbstractParams basic, String url) throws Exception {
		Map<String, String> params = basic.getParams();
		url = HttpReqUtil.setParmas(params, url, "") + "#wechat_redirect";
		return url;
	}

	/**
	 * 获取网页授权凭证
	 *
	 * @param basic
	 * @param url
	 * @return
	 */
	public AuthAccessToken getAuthAccessToken(AbstractParams basic, String url) {
		AuthAccessToken authAccessToken = null;
		// 获取网页授权凭证
		try {
			if (StringUtils.isEmpty(url)) {
				url = WechatUrlConfig.GET_OAUTH_TOKEN_URL;
			}
			String result = HttpReqUtil.HttpsDefaultExecute(Constant.GET_METHOD, url, basic.getParams(), null, null);
			authAccessToken = JsonUtil.fromJsonString(result, AuthAccessToken.class);
		} catch (Exception e) {
			logger.debug("error" + e.getMessage());
		}
		return authAccessToken;
	}

	/**
	 * 刷新网页授权验证
	 *
	 * @param basic
	 *            参数
	 * @param url
	 *            请求路径
	 * @return
	 */
	public AuthAccessToken refreshAuthAccessToken(AbstractParams basic, String url) {
		AuthAccessToken authAccessToken = null;
		// 刷新网页授权凭证
		try {
			if (StringUtils.isEmpty(url)) {
				url = WechatUrlConfig.REFRESH_OAUTH_TOKEN_URL;
			}
			String result = HttpReqUtil.HttpsDefaultExecute(Constant.GET_METHOD, url, basic.getParams(), null, null);
			authAccessToken = JsonUtil.fromJsonString(result, AuthAccessToken.class);
		} catch (Exception e) {
			logger.debug("error" + e.getMessage());
		}
		return authAccessToken;
	}

	/**
	 * 通过网页授权获取用户信息
	 *
	 * @param accessToken
	 * @param openid
	 * @return
	 */
	public AuthUserInfo getAuthUserInfo(String accessToken, String openid) {
		AuthUserInfo authUserInfo = null;
		// 通过网页授权获取用户信息
		Map<String, String> params = new TreeMap<>();
		params.put("openid", openid);
		params.put("access_token", accessToken);
		String result = HttpReqUtil.HttpsDefaultExecute(Constant.GET_METHOD, WechatUrlConfig.SNS_USERINFO_URL, params,
				null, null);
		try {
			authUserInfo = JsonUtil.fromJsonString(result, AuthUserInfo.class);
		} catch (JsonSyntaxException e) {
			logger.debug("transfer exception");
		}
		return authUserInfo;
	}

	/**
	 * 检验授权凭证（access_token）是否有效
	 *
	 * @param accessToken
	 *            网页授权接口调用凭证
	 * @param openid
	 *            用户的唯一标识
	 * @return { "errcode":0,"errmsg":"ok"}表示成功 { "errcode":40003,"errmsg":"invalid
	 *         openid"}失败
	 */
	public ResultState authToken(String accessToken, String openid) {
		ResultState state = null;
		Map<String, String> params = new TreeMap<>();
		params.put("access_token", accessToken);
		params.put("openid", openid);
		String jsonResult = HttpReqUtil.HttpDefaultExecute(Constant.GET_METHOD,
				WechatUrlConfig.CHECK_SNS_AUTH_STATUS_URL, params, "", null);
		state = JsonUtil.fromJsonString(jsonResult, ResultState.class);
		return state;
	}

	/**
	 * 获取jsapi_ticket 调用微信JS接口的临时票据
	 *
	 * @return
	 */
	@Override
	public String getTicket() {
        String accessToken = getAccessToken();
        String result = redisTemplate.opsForValue().get(Constant.WECHAT_JS_SDK_TICKET);
        if (StringUtils.isNotBlank(result)) {
            return result;
        }
		JsapiTicket jsapiTicket = null;
		Map<String, String> params = new TreeMap<>();
		params.put("access_token", accessToken);
		params.put("type", "jsapi");
		String res = HttpReqUtil.HttpDefaultExecute(Constant.GET_METHOD, WechatUrlConfig.GET_TICKET_URL, params,
				"", null);
		jsapiTicket = JsonUtil.fromJsonString(res, JsapiTicket.class);
		if (jsapiTicket.getErrcode() == 0) {
			result = jsapiTicket.getTicket();
            redisTemplate.opsForValue().set(Constant.WECHAT_JS_SDK_TICKET, result, jsapiTicket.getExpires_in() - 20, TimeUnit.SECONDS);
		}
		return result;
	}

    /**
     * 获得随机字符串
     *
     * @return
     */
     private String createNonceStr() {
        return MD5Util.MD5Encode(String.valueOf(ThreadLocalRandom.current().nextInt(10000)), Constant.DEFAULT_CHARACTER_ENCODING);
     }


    /**
     * js-sdk接口注入权限验证配置信息
     * @param url
     * @return
     */
    @Override
    public JsSDKConfig jsSDKConfig(String url) {
        JsSDKConfig jsSDKConfig = new JsSDKConfig();
        String ticket = getTicket();
        String nonceStr = createNonceStr();
        long timestamp = System.currentTimeMillis() / 1000;
        String signature = SignatureUtil.createSign(nonceStr, ticket, timestamp, url);
        jsSDKConfig.setNonceStr(nonceStr);
        jsSDKConfig.setTimestamp(timestamp);
        jsSDKConfig.setSignature(signature);
        jsSDKConfig.setAppId(wechatConfig.appId);
        return jsSDKConfig;
    }
}
